package com.qiqv.demo.transformer;

import javassist.*;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;

/**
 * @author hjr
 * @version 1.0
 * @date 2023/12/10 15:32
 */
public class LogTransformer  implements ClassFileTransformer {

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        if(!className.contains("com/qiqv") || !className.contains("ServiceImpl")){
            return classfileBuffer;
         }
        try {
            return agentForRunTimeStatistical(loader,className);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }



    private byte[] agentForRunTimeStatistical(ClassLoader loader,String className) throws Exception {
        String classPath = className.replaceAll("/",".");
        CtClass ctClass = this.findClassInClassPool(loader,classPath,true);
        if(ctClass == null){
            System.out.println("在类加载器中找不到："+classPath);
            return null;
        }
        System.out.println("在类加载器中找到了："+classPath);
        // 只对本类方法进行拦截，不处理父类方法
        CtMethod[] methods = ctClass.getDeclaredMethods();
        
        for (CtMethod method : methods) {
            CtMethod methodCopy = CtNewMethod.copy(method, ctClass, new ClassMap());
            String agentMethodName = method.getName() + "$agent";
            method.setName(agentMethodName);
            StringBuffer body = new StringBuffer("{\n")
                    .append("System.out.println(\"start statistical method...\");\n")
                    .append("long begin = System.currentTimeMillis();\n")
                    .append("try{\n")
                    .append("return ($r)" + agentMethodName + "($$);\n")
                    .append("}finally {\n")
                    .append("long executeTime = System.currentTimeMillis() - begin;\n")
                    .append("System.out.println(\"" + methodCopy.getName() + " method total execute time is \" + executeTime);\n")
                    .append("}\n")
                    .append("}");
            methodCopy.setBody(body.toString());
            ctClass.addMethod(methodCopy);
        }
        return ctClass.toBytecode();
    }

    private CtClass findClassInClassPool(ClassLoader loader, String classPath,boolean firstTry) throws NotFoundException {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = null;
        try {
            ctClass = pool.get(classPath);
        }catch (NotFoundException e){
            if(firstTry){
                System.out.println("can not find "+ classPath +" in ClassPool first time");
                pool.appendClassPath(new LoaderClassPath(loader));
                ctClass = findClassInClassPool(loader,classPath,false);
            }
        }
        return ctClass;
    }

    
}
